<?php

namespace Hilaoyu\ComputeCloudClient\Api;

use AlibabaCloud\SDK\Ecs\V20140526\Models\AllocatePublicIpAddressRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\AttachDiskRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\AttachNetworkInterfaceRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\AuthorizeSecurityGroupEgressRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\AuthorizeSecurityGroupRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\CreateCommandRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\CreateDiskRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\CreateInstanceRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\CreateNetworkInterfaceRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\CreateSecurityGroupRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\CreateSnapshotRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DeleteCommandRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DeleteDiskRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DeleteInstancesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DeleteNetworkInterfaceRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DeleteSecurityGroupRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeCommandsRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeDisksRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeImagesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeInstancesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeInstanceStatusRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeInstanceTypesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeInstanceVncUrlRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeInvocationsRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeNetworkInterfacesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeRegionsRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeSecurityGroupsRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DescribeZonesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DetachDiskRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\DetachNetworkInterfaceRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\InvokeCommandRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\ModifyDiskAttributeRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\ModifyInstanceAttributeRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\ModifyInstanceVncPasswdRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\RebootInstancesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\ReplaceSystemDiskRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\RevokeSecurityGroupEgressRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\RevokeSecurityGroupRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\RunCommandRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\StartInstancesRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\StopInstanceRequest;
use AlibabaCloud\SDK\Ecs\V20140526\Models\StopInstancesRequest;
use AlibabaCloud\Tea\Model;
use Hilaoyu\ComputeCloudClient\AssetModel;
use Hilaoyu\ComputeCloudClient\AssetModels;
use Hilaoyu\ComputeCloudClient\Interfaces\ApiEcsInterface;
use Hilaoyu\Utils\UtilArr;
use Hilaoyu\ComputeCloudClient\AliyunApi;
use Hilaoyu\Utils\UtilHttp;
use Hilaoyu\Utils\UtilStr;
use Hilaoyu\Utils\UtilSupport;

/**
 * Class AliEcs
 * @package Hilaoyu\ComputeCloudClient\Api
 * @property  \AlibabaCloud\SDK\Ecs\V20140526\Ecs $client
 */
class AliEcs extends AliApiBase implements ApiEcsInterface
{

    public function regionsDescribe():array
    {
        $models = [];
        try {
            $request = new DescribeRegionsRequest([]);
            $result = $this->client->describeRegions($request);
            $data = (array)UtilArr::get($result->body->toMap(), 'Regions.Region');
            foreach ($data as $datum){
                $id = UtilArr::get($datum,'RegionId');
                if(!$id){
                    continue;
                }
                $models[] = ["id"=>$id,"name"=>UtilArr::get($datum,'LocalName')];
            }
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function zonesDescribe($regionId = ''):array
    {
        $regionId = $this->getRegionId($regionId);
        $models = [];
        if (!$regionId) {
            return [];
        }
        try {
            $request = new DescribeZonesRequest([
                'regionId' => $regionId,
            ]);
            $result = $this->client->describeZones($request);


            $data = (array)UtilArr::get($result->body->toMap(), 'Zones.Zone');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'ZoneId');
                if(!$id){
                    continue;
                }
                $models[] = ["id"=>$id,"name"=>UtilArr::get($datum,'LocalName')];
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    //安全组
    public function securityGroupCreate(string $securityGroupName = null, string $vpcId = null, string $securityGroupType = 'normal', $regionId = '', $resourceGroupId = null):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new CreateSecurityGroupRequest([
                'regionId' => $regionId,
                'securityGroupName' => $securityGroupName,
                'vpcId' => $vpcId,
                'securityGroupType' => $securityGroupType,
                'resourceGroupId' => $resourceGroupId,
            ]);
            $result = $this->client->createSecurityGroup($request);

            $data =  $result->body->toMap();

            $id = UtilArr::get($data,'SecurityGroupId');
            if(!$id){
                throw new \Exception('返回数据错误');
            }


            $model = new AssetModel();
            $model->type ='sg';
            $model->asset_id = $id;
            $model->name = (string)$securityGroupName;
            $model->os_type = (string)$securityGroupType;
            $model->region_id = (string)$regionId;
            $model->bind_asset_id = (string)$vpcId;
            return $model;


        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function securityGroupDelete(string $securityGroupId, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        $securityGroupId = trim($securityGroupId);
        if(!$securityGroupId){
            return false;
        }
        if(!$this->securityGroupDescribe($securityGroupId)){
            return true;
        }
        try {
            $request = new DeleteSecurityGroupRequest([
                'regionId' => $regionId,
                'securityGroupId' => $securityGroupId,
            ]);
            $result = $this->client->deleteSecurityGroup($request);

            $data= $result->body->toMap();
            return (bool)$data;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function securityGroupsDescribe(array $securityGroupIds = [], string $securityGroupName = null, string $vpcId = null,int $pageSize=100,int $pageNumber=1, $regionId = '', $resourceGroupId = null):AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {
            $request = new DescribeSecurityGroupsRequest([
                'regionId' => $regionId,
                'securityGroupIds' => !empty($securityGroupIds) ? json_encode($securityGroupIds) : null,
                'securityGroupName' => $securityGroupName,
                'vpcId' => $vpcId,
                'pageSize' => $pageSize,
                'pageNumber' => $pageNumber,
                'resourceGroupId' => $resourceGroupId,
            ]);
            $result = $this->client->describeSecurityGroups($request);
            $data = (array)UtilArr::get($result->body->toMap(), 'SecurityGroups.SecurityGroup');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'SecurityGroupId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='sg';
                $model->asset_id = $id;
                $model->name = UtilArr::get($datum,'SecurityGroupName');
                $model->os_type = UtilArr::get($datum,'SecurityGroupType');
                $model->bind_asset_id = UtilArr::get($datum,'VpcId');
                $model->region_id = (string)$regionId;

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function securityGroupDescribe(string $securityGroupId, $regionId = ''):?AssetModel
    {
        $models = $this->securityGroupsDescribe([$securityGroupId],null,null,10,1,$regionId);
        if(!$models){
            return null;
        }


        return $models->first(function ($item) use ($securityGroupId){
            return  $item->asset_id == $securityGroupId;
        });
        return null;
    }

    /**
     * 添加入方向规则
     * @param $securityGroupId
     * @param string $ipProtocol tcp|udp|icmp|gre|all
     * @param string $portRange 目的端安全组开放的传输层协议相关的端口范围。取值范围: TCP/UDP协议：取值范围为1~65535。使用斜线（/）隔开起始端口和终止端口。例如：1/200;ICMP协议：-1/-1;GRE协议：-1/-1;IpProtocol取值为all：-1/-1
     * @param string $policy accept|drop
     * @param string $sourceCidrIp 源端IPv4 CIDR地址段。支持CIDR格式和IPv4格式的IP地址范围
     * @param string $sourcePortRange 源端安全组开放的传输层协议相关的端口范围。取值范围: TCP/UDP协议：取值范围为1~65535。使用斜线（/）隔开起始端口和终止端口。例如：1/200;ICMP协议：-1/-1;GRE协议：-1/-1;IpProtocol取值为all：-1/-1
     * @param string $destCidrIp 目的端IPv4 CIDR地址段。支持CIDR格式和IPv4格式的IP地址范围
     * @param int $priority 1~100
     * @param string $regionId
     * @return array
     */
    public function securityGroupAuthorize($securityGroupId, $portRange, $policy = 'accept', $sourceCidrIp = '0.0.0.0/0', $ipProtocol = 'tcp', $sourcePortRange = null, $destCidrIp = null, $priority = 1, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new AuthorizeSecurityGroupRequest([
                'regionId' => $regionId,
                'securityGroupId' => $securityGroupId,
                'ipProtocol' => $ipProtocol,
                'portRange' => $portRange,
                'policy' => $policy,
                'priority' => $priority,
                'sourceCidrIp' => $sourceCidrIp,
                'sourcePortRange' => $sourcePortRange,
                'destCidrIp' => $destCidrIp,
            ]);
            $result = $this->client->authorizeSecurityGroup($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function securityGroupRevoke($securityGroupId, $portRange, $policy = 'accept', $sourceCidrIp = '0.0.0.0/0', $ipProtocol = 'tcp', $sourcePortRange = null, $destCidrIp = null,  $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new RevokeSecurityGroupRequest([
                'regionId' => $regionId,
                'securityGroupId' => $securityGroupId,
                'ipProtocol' => $ipProtocol,
                'portRange' => $portRange,
                'policy' => $policy,
                'sourceCidrIp' => $sourceCidrIp,
                'sourcePortRange' => $sourcePortRange,
                'destCidrIp' => $destCidrIp,
            ]);
            $result = $this->client->revokeSecurityGroup($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function securityGroupEgressAuthorize($securityGroupId, $portRange, $policy = 'accept', $destCidrIp = '0.0.0.0/0', $ipProtocol = 'tcp', $sourcePortRange = null, $sourceCidrIp = null, $priority = 1, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new AuthorizeSecurityGroupEgressRequest([
                'regionId' => $regionId,
                'securityGroupId' => $securityGroupId,
                'ipProtocol' => $ipProtocol,
                'portRange' => $portRange,
                'policy' => $policy,
                'priority' => $priority,
                'sourceCidrIp' => $sourceCidrIp,
                'sourcePortRange' => $sourcePortRange,
                'destCidrIp' => $destCidrIp,
            ]);
            $result = $this->client->authorizeSecurityGroupEgress($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function securityGroupEgressRevoke($securityGroupId, $portRange, $policy = 'accept', $destCidrIp = '0.0.0.0/0', $ipProtocol = 'tcp', $sourcePortRange = null, $sourceCidrIp = null,  $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new RevokeSecurityGroupEgressRequest([
                'regionId' => $regionId,
                'securityGroupId' => $securityGroupId,
                'ipProtocol' => $ipProtocol,
                'portRange' => $portRange,
                'policy' => $policy,
                'sourceCidrIp' => $sourceCidrIp,
                'sourcePortRange' => $sourcePortRange,
                'destCidrIp' => $destCidrIp,
            ]);
            $result = $this->client->revokeSecurityGroupEgress($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }


    /**
     * @param null $instanceType 指定实例类型可以使用的镜像。ecs.g5.large
     * @param null $osType 镜像的操作系统类型 windows , linux
     * @param string $architecture 镜像的体系架构。取值范围： i386 x86_64
     * @param null $imageName 镜像名称,可用*号通配  centos*
     * @param int $pageNumber
     * @param int $pageSize
     * @param string $regionId
     * @return array
     */
    public function imagesDescribe(string $imageName = null,$imageId=null, $instanceType = null, $osType = null, $architecture = null, $regionId = '', $zoneId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        if($imageName){
            $imageName = '*'.$imageName.'*';
        }
        $models = new AssetModels();
        try {
            $requestData = [
                'regionId' => $regionId,
                'pageSize' => 100,
            ];
            if($instanceType){
                $requestData['instanceType'] = $instanceType;
            }
            if($osType){
                $requestData['OSType'] = $osType;
            }
            if($architecture){
                $requestData['architecture'] = $architecture;
            }
            if($imageName){
                $requestData['imageName'] = $imageName;
            }
            if($imageId){
                $requestData['imageId'] = $imageId;
            }

            $request = new DescribeImagesRequest($requestData);

            $result = $this->client->describeImages($request);


            $data = (array)UtilArr::get($result->body->toMap(), 'Images.Image');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'ImageId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='image';
                $model->asset_id = $id;
                $model->name = UtilArr::get($datum,'ImageName');
                $model->os_type = UtilArr::get($datum,'OSType');
                $model->os = UtilArr::get($datum,'OSName');

                $models->push($model);
            }

        } catch (\Exception $exception) {

            $this->setError($exception->getMessage());
        }

        return $models;
    }


    public function imageDescribe(string $imageId, $regionId = '', $zoneId = ''):?AssetModel
    {
        $models = $this->imagesDescribe(null,$imageId);
        if(!$models){
            return null;
        }

        return $models->first(function ($item) use ($imageId){
            return  $item->asset_id == $imageId;
        });
        return null;
    }

    public function instanceTypesDescribe(array $instanceTypes=[],string $zoneId='',string $regionId = ''):AssetModels
    {
        $models = new AssetModels();
        try {
            $request = new DescribeInstanceTypesRequest([
                'instanceTypes' => !empty($instanceTypes) ? $instanceTypes : null,
            ]);
            $result = $this->client->describeInstanceTypes($request);
            $data = (array)UtilArr::get($result->body->toMap(), 'InstanceTypes.InstanceType');
            foreach ($data as $datum){
                $id = UtilArr::get($datum,'InstanceTypeId');
                if(!$id){
                    continue;
                }
                $model = new AssetModel();
                $model->type = 'ecs_type';
                $model->asset_id = $id;
                $model->cpu = UtilArr::get($datum,'CpuCoreCount');
                $model->memory = UtilArr::get($datum,'MemorySize');
                $model->name = UtilArr::get($datum,'InstanceTypeFamily').'-'.UtilArr::get($datum,'CpuCoreCount').'核'.UtilArr::get($datum,'MemorySize').'G';
                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function instanceTypeDescribe(string $instanceType,string $zoneId='',string $regionId = ''):?AssetModel
    {
        $models = $this->instanceTypesDescribe([$instanceType]);
        if(!$models){
            return null;
        }

        return $models->first(function ($item) use ($instanceType){
            return  $item->asset_id == $instanceType;
        });
        return null;
    }



    //实例

    /**
     * 创建实例
     * https://next.api.aliyun.com/api/Ecs/2014-05-26/CreateInstance?params={}
     * @param string $instanceType 实例规格
     * @param string $imageId 镜像文件ID
     * @param string $password 密码
     * @param string $instanceName 实例名 允许大小写英文字母、数字和短横线（-）
     * @param string $vSwitchId 虚拟交换机ID
     * @param int $internetMaxBandwidth 公网带宽峰值 0为不需要公网IP
     * @param null $securityGroupId 安全组ID
     * @param int $period 时长
     * @param string $periodUnit 时长单位 取值范围：Week和Month（默认
     * @param string $instanceChargeType 实例的付费方式。取值范围： PrePaid：包年包月,PostPaid 按量付费;选择该类付费方式时，您必须确认自己的账号支持余额支付/信用支付，否则将返回 InvalidPayMethod的错误提示。 PostPaid（默认）：按量付费。
     * @param string $internetChargeType 网络计费类型。取值范围：PayByBandwidth：按固定带宽计费。 PayByTraffic（默认）：按使用流量计费
     * @param array $request_data 其它数据
     * @param string $regionId
     * @param null $resourceGroupId 资源组ID
     * @return array
     */
    public function instanceCreate(string $instanceType, string $imageId, string $password, string $instanceName = null,string $vSwitchId = null,$internetMaxBandwidth = 0,  $securityGroupId = null, $period = 1, $periodUnit = 'Month', $instanceChargeType = 'PostPaid', $internetChargeType = 'PayByTraffic', int $systemDiskSize=null,$zoneId = null,  $regionId = '', $resourceGroupId = null):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);
        $request_data = [
            'regionId' => $regionId,
            'instanceType' => $instanceType,
            'imageId' => $imageId,
            'password' => $password,
            'instanceChargeType' => $instanceChargeType,
            'internetChargeType' => $internetChargeType,
            'resourceGroupId' => $resourceGroupId,
            'securityGroupId' => $securityGroupId,
            'vSwitchId' => $vSwitchId,
            'instanceName' => $instanceName,
            'hostName' => $instanceName,
            'amount' => 1,
            'uniqueSuffix' => 'true',
            'securityEnhancementStrategy' => 'false',
            'period' => $period,
            'periodUnit' => $periodUnit,
            'internetMaxBandwidthOut' => $internetMaxBandwidth,
            'zoneId' => $zoneId ? trim($zoneId) : null,
        ];

        if(($systemDiskSize = intval($systemDiskSize)) > 0){
            $systemDisk = new CreateInstanceRequest\systemDisk([
                "size" => $systemDiskSize
            ]);
            $request_data['systemDisk'] = $systemDisk;
        }


        try {

            $image = $this->imageDescribe($imageId);
            if(!$image){
                throw new \Exception('镜像不存在');
            }

            $instanceTypeModel = $this->instanceTypeDescribe($instanceType);
            if(!$instanceTypeModel){
                throw new \Exception('实例规格不存在不存在');
            }


            $request = new CreateInstanceRequest($request_data);
            $result = $this->client->createInstance($request);

            $data =  $result->body->toMap();

            $id = UtilArr::get($data,'InstanceId');
            if(!$id){
                throw new \Exception('返回数据错误');
            }



            $model = new AssetModel();
            $model->type ='ecs';
            $model->asset_id = $id;
            $model->name = (string)$instanceName;
            $model->instance_type = (string)$instanceType;
            $model->region_id = (string)$regionId;
            $model->os_password = (string)$password;

            $model->os_type = $image->os_type;
            $model->os = $image->name;
            $model->os_image = $image->asset_id;
            $model->cpu = $instanceTypeModel->cpu;
            $model->memory = $instanceTypeModel->memory;
            $model->bandwidth = (int)$internetMaxBandwidth;

            $model->os_account = 'windows' == UtilStr::contains($model->os_type,'Windows',false) ? 'administrator' : 'root';

            $model->status = 'Pending';


            if(intval($internetMaxBandwidth) > 0){
                $complete = false;
                UtilSupport::reTry(function () use ( &$model, &$complete) {
                    if (!$temp = $this->instanceDescribe($model->asset_id)) {
                        $this->setError('获取ecs状态失败') ;
                        return true;
                    }
                    if ('Pending' != $model->status) {
                        $model->merge($temp);
                        $complete = true;
                        return true;
                    }
                    return false;
                }, 10, 12);

                if ($complete) {
                    $publicIpAddrss = $this->publicIpAddressAllocate($id,$internetMaxBandwidth,$regionId);
                    if(!$publicIpAddrss){
                        //$this->instancesDelete([$id],true,$regionId);
                        $this->setError('分配公网IP出错');
                    }
                    $model->public_ip = $publicIpAddrss;
                }


            }

            return $model;
        } catch (\Exception $exception) {

            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function publicIpAddressAllocate(string $instanceId,int $bandwidth=100, $regionId = ''):string{
        $regionId = $this->getRegionId($regionId);
        $ipAddress = '';
        try {
            $request = new AllocatePublicIpAddressRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->allocatePublicIpAddress($request);

            $ipAddress =  UtilArr::get($result->body->toMap(), 'IpAddress');
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $ipAddress;
    }

    public function instancesDescribe(array $instanceIds=[],$vSwitchId=null,$zoneId="", $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {
            $request = new DescribeInstancesRequest([
                'regionId' => $regionId,
                'instanceIds' => json_encode($instanceIds),
                'vSwitchId' => $vSwitchId,
            ]);
            $result = $this->client->describeInstances($request);


            $data = (array)UtilArr::get($result->body->toMap(), 'Instances.Instance');
            //dump($data);
            foreach ($data as $datum){
                $id = UtilArr::get($datum,'InstanceId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $id;
                $model->name = UtilArr::get($datum,'InstanceName');
                $model->os_type = UtilArr::get($datum,'OSType');
                $model->instance_type = UtilArr::get($datum,'InstanceType');
                $model->region_id = UtilArr::get($datum,'RegionId');
                $model->cpu = UtilArr::get($datum,'Cpu');
                $model->memory = UtilArr::get($datum,'Memory');
                $model->bandwidth = UtilArr::get($datum,'InternetMaxBandwidthOut');
                $model->primary_ip = UtilArr::get($datum,'NetworkInterfaces.NetworkInterface.0.PrimaryIpAddress');
                $model->second_ip = UtilArr::get($datum,'NetworkInterfaces.NetworkInterface.1.PrimaryIpAddress');
                $model->public_ip = UtilArr::get($datum,'PublicIpAddress.IpAddress.0',UtilArr::get($datum,'EipAddress.IpAddress',''));
                $model->bind_asset_id = trim(UtilArr::get($datum,'EipAddress.AllocationId',''));
                $model->os_type = UtilArr::get($datum,'OSType');
                $model->os = UtilArr::get($datum,'OSName');
                $model->os_image = UtilArr::get($datum,'ImageId');
                $model->os_account = 'windows' == UtilStr::contains($model->os_type,'Windows',false) ? 'administrator' : 'root';
                $model->status = UtilArr::get($datum,'Status');
                $model->pay_change_type = UtilArr::get($datum,'InstanceChargeType');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }
    public function instanceDescribe(string $instanceId,$zoneId="", $regionId = ''):?AssetModel
    {
        $models = $this->instancesDescribe([$instanceId],null,$regionId);
        if(!$models){
            return null;
        }


        return $models->first(function ($item) use ($instanceId){
           return  $item->asset_id == $instanceId;
        });
        return null;
    }

    public function instancesStart(array $instanceId, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {
            $request = new StartInstancesRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
                'batchOptimization' => 'SuccessFirst',
            ]);
            $result = $this->client->startInstances($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'InstanceResponses.InstanceResponse');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'InstanceId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $id;
                $model->status = UtilArr::get($datum,'CurrentStatus');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function instancesStatusDescribe(array $instanceId, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {
            $request = new DescribeInstanceStatusRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->describeInstanceStatus($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'InstanceStatuses.InstanceStatus');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'InstanceId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $id;
                $model->status = UtilArr::get($datum,'Status');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function instanceStatusDescribe(string $instanceId, $regionId = ''):?AssetModel
    {
        $models = $this->instancesStatusDescribe([$instanceId], $regionId);
        if(!$models){
            return null;
        }

        return $models->first(function ($item) use ($instanceId){
            return  $item->asset_id == $instanceId;
        });
    }

    public function instancesStop(array $instanceId, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {
            $request = new StopInstancesRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
                'batchOptimization' => 'SuccessFirst',
            ]);
            $result = $this->client->stopInstances($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'InstanceResponses.InstanceResponse');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'InstanceId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $id;
                $model->status = UtilArr::get($datum,'CurrentStatus');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function instancesReboot(array $instanceId, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);

        $models = new AssetModels();
        try {
            $request = new RebootInstancesRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
                'batchOptimization' => 'SuccessFirst',
            ]);
            $result = $this->client->rebootInstances($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'InstanceResponses.InstanceResponse');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'InstanceId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $id;
                $model->status = UtilArr::get($datum,'CurrentStatus');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function instancesDelete(array $instanceId, $force = false, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new DeleteInstancesRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
                'force' => $force ? 'true' : 'false',
            ]);
            $result = $this->client->deleteInstances($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function instanceAttributeModify(string $instanceId, string $password='',string $name='',string $hostName='',$userData=null, $regionId = ''):bool {
        $regionId = $this->getRegionId($regionId);
        $name = trim($name);
        $hostName = trim($hostName);
        try {
            $params = [
                'regionId' => $regionId,
                'instanceId' => $instanceId,
            ];
            if($password){
                $params['password'] = $password;
            }
            if($name){
                $params['instanceName'] = $name;
            }
            if($hostName){
                $params['hostName'] = $hostName;
            }
            if($userData){
                $params['userData'] = $userData;
            }
            $request = new ModifyInstanceAttributeRequest($params);
            $result = $this->client->modifyInstanceAttribute($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }


    public function instanceVncPasswdModify(string $instanceId, string $vncPassword, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new ModifyInstanceVncPasswdRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
                'vncPassword' => $vncPassword,
            ]);
            $result = $this->client->modifyInstanceVncPasswd($request);

            if((bool)$result->body->toMap()){
                $model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $instanceId;
                $model->manage_password = $vncPassword;
                return $model;
            }
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function instanceVncUrlDescribe(string $instanceId, string $vncPassword = '', $regionId = ''):string
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $instance = $this->instanceDescribe($instanceId);
            if(!$instance){
                throw new \Exception('实例不存在');
            }
            $oSType = $instance->os_type;

            $request = new DescribeInstanceVncUrlRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId
            ]);
            $result = $this->client->describeInstanceVncUrl($request);

            $vncUrl = UtilArr::get($result->body->toMap(), 'VncUrl');
            if (!$vncUrl) {
                throw new \Exception('获取vnc地址失败');
            }



            return UtilHttp::buildUrl('https://g.alicdn.com/aliyun/ecs-console-vnc2/0.0.8/index.html', [
                'vncUrl' => $vncUrl,
                'instanceId' => $instanceId,
                'isWindows' => ('windows'==$oSType)?'true':'false',
                'password' => $vncPassword,
            ]);
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return '';
    }


    public function systemDiskReplace(string $instanceId, string $imageId, string $password, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {

            $image = $this->imageDescribe($imageId);
            if(!$image){
                throw new \Exception('镜像不存在');
            }

            $model = $this->instanceDescribe($instanceId);
            if(!$model){
                throw new \Exception('实例不存在');
            }

            if('Stopped' != $model->status){
                throw new \Exception('请先停止实例');
            }


            $request = new ReplaceSystemDiskRequest([
                'regionId' => $regionId,
                'instanceId' => $instanceId,
                'imageId' => $imageId,
                'password' => $password,
            ]);
            $result = $this->client->replaceSystemDisk($request);



            if((bool)$result->body->toMap()){
                /*$model = new AssetModel();
                $model->type ='ecs';
                $model->asset_id = $instanceId;*/
                $model->os_password = $password;

                $model->os_type = $image->os_type;
                $model->os = $image->name;
                $model->os_image = $image->asset_id;
                $model->status = 'Pending';

                $model->os_account = UtilStr::contains($model->os_type,'Windows',false) ? 'administrator' : 'root';
                return $model;
            }
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function systemDiskSnapshotCreate(string $instanceId, string $name='', $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $instanceId = trim($instanceId);
        if (!$instanceId) {
            return false;
        }


        try {
            $disks = $this->disksDescribe([],$instanceId,$regionId);

            $sysDisk = $disks->first(function ($disk){
                return "system" === $disk->os_type;
            });
            if(!$sysDisk){
                throw new \Exception("没找到系统磁盘");
            }
            return $this->diskSnapshotCreate($sysDisk->asset_id,$name,$regionId);

        } catch (ServiceResponseException $exception) {
            $this->setError($exception->getErrorMsg());
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }

        return false;
    }
    //


    public function networkInterfaceCreate(string $vSwitchId,string $networkInterfaceName=null,array $securityGroupIds=[], $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new CreateNetworkInterfaceRequest([
                'regionId' => $regionId,
                'vSwitchId' => $vSwitchId,
                'securityGroupIds' => $securityGroupIds,
                'networkInterfaceName' => $networkInterfaceName,
            ]);
            $result = $this->client->createNetworkInterface($request);

            $data =  $result->body->toMap();

            $id = UtilArr::get($data,'NetworkInterfaceId');
            if(!$id){
                throw new \Exception('返回数据错误');
            }


            $model = new AssetModel();
            $model->type ='eni';
            $model->asset_id = $id;

            $model->name = UtilArr::get($data,'NetworkInterfaceName');
            $model->region_id = (string)$regionId;
            $model->primary_ip = UtilArr::get($data,'PrivateIpAddress');


            $model->status = UtilArr::get($data,'Status');
            return $model;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }



    public function networkInterfacesDescribe(array $networkInterfaceId=[],$instanceId=null, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);

        $models = new AssetModels();
        try {
            $request = new DescribeNetworkInterfacesRequest([
                'regionId' => $regionId,
                'networkInterfaceId' => !empty($networkInterfaceId) ? $networkInterfaceId : null,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->describeNetworkInterfaces($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'NetworkInterfaceSets.NetworkInterfaceSet');

            //dump($data);
            foreach ($data as $datum){
                $id = UtilArr::get($datum,'NetworkInterfaceId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='eni';
                $model->asset_id = $id;

                $model->name = UtilArr::get($data,'NetworkInterfaceName');
                $model->region_id = (string)$regionId;
                $model->primary_ip = UtilArr::get($data,'PrivateIpAddress');
                $model->bind_asset_id = UtilArr::get($datum,'InstanceId');

                $model->status = UtilArr::get($data,'Status');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function networkInterfaceDescribe(string $networkInterfaceId, $regionId = ''):?AssetModel
    {
        $models = $this->networkInterfacesDescribe([$networkInterfaceId],null, $regionId);
        if(!$models){
            return null;
        }

        return $models->first(function ($item) use ($networkInterfaceId){
            return  $item->asset_id == $networkInterfaceId;
        });
    }

    public function networkInterfaceDelete(string $networkInterfaceId, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        $networkInterfaceId = trim($networkInterfaceId);
        if(!$networkInterfaceId){
            return false;
        }
        if(!$this->networkInterfaceDescribe($networkInterfaceId)){
            return true;
        }
        try {
            $request = new DeleteNetworkInterfaceRequest([
                'regionId' => $regionId,
                'networkInterfaceId' => $networkInterfaceId,
            ]);
            $result = $this->client->deleteNetworkInterface($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function networkInterfaceAttach(string $networkInterfaceId,string $instanceId, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $networkInterfaceModel=$this->networkInterfaceDescribe($networkInterfaceId,$regionId);
            if(!$networkInterfaceModel){
                throw new \Exception('弹性网卡不存在');
            }
            $instanceModel = $this->instanceDescribe($instanceId,$regionId);
            if(!$instanceModel){
                throw new \Exception('实例不存在');
            }
            $request = new AttachNetworkInterfaceRequest([
                'regionId' => $regionId,
                'networkInterfaceId' => $networkInterfaceId,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->attachNetworkInterface($request);

             $instanceModel->second_ip = $networkInterfaceModel->primary_ip;
             return $instanceModel;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }
    public function networkInterfaceDetach(string $networkInterfaceId,string $instanceId, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $instanceModel = $this->instanceDescribe($instanceId,$regionId);
            if(!$instanceModel){
                throw new \Exception('实例不存在');
            }

            $request = new DetachNetworkInterfaceRequest([
                'regionId' => $regionId,
                'networkInterfaceId' => $networkInterfaceId,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->detachNetworkInterface($request);

            $instanceModel->second_ip = '';
            return$instanceModel;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function commandCreate(string $commandContent,string $name,string $type='RunShellScript', $regionId = ''):?AssetModel{
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new CreateCommandRequest([
                'regionId' => $regionId,
                'name' => $name,
                'type' => $type,
                'commandContent' => base64_encode($commandContent),
            ]);
            $result = $this->client->createCommand($request);

            $data =  $result->body->toMap();

            $id = UtilArr::get($data,'CommandId');
            if(!$id){
                throw new \Exception('返回数据错误');
            }


            $model = new AssetModel();
            $model->type ='cmd';
            $model->asset_id = $id;

            $model->name = (string)$name;
            $model->os_type = (string)$type;
            $model->cmd_content = (string)$commandContent;
            $model->region_id = (string)$regionId;


            $model->status = '0';
            return $model;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function commandsDescribe(string $commandId=null,string $name=null,string $type=null, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);

        $models = new AssetModels();
        try {
            $request = new DescribeCommandsRequest([
                'regionId' => $regionId,
                'commandId' => !empty($commandId) ? $commandId : null,
                'name' => !empty($name) ? $name : null,
                'type' => !empty($type) ? $type : null,
            ]);
            $result = $this->client->describeCommands($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'Commands.Command');

            foreach ($data as $datum){
                $id = UtilArr::get($datum,'CommandId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='cmd';
                $model->asset_id = $id;
                $model->name = UtilArr::get($datum,'Name');
                $model->os_type = UtilArr::get($datum,'Type');
                $model->cmd_content = @base64_decode(UtilArr::get($datum,'CommandContent'));
                $model->region_id = (string)$regionId;


                $model->status = 'Available';
                //$model->status = UtilArr::get($datum,'InvokeTimes');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function commandDescribe(string $commandId, $regionId = ''):?AssetModel
    {
        $models = $this->commandsDescribe($commandId,null,null,$regionId );
        if(!$models){
            return null;
        }

        return $models->first(function ($item) use ($commandId){
            return  $item->asset_id == $commandId;
        });
    }

    public function commandDelete(string $commandId, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);

        $commandId = trim($commandId);
        if(!$commandId){
            return false;
        }

        if(!$this->commandDescribe($commandId)){
            return true;
        }

        try {
            $request = new DeleteCommandRequest([
                'regionId' => $regionId,
                'commandId' => $commandId,
            ]);
            $result = $this->client->deleteCommand($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }
    public function commandInvoke(string $commandId,array $instanceId,string $frequency=null,$regionId = ''):?string
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new InvokeCommandRequest([
                'regionId' => $regionId,
                'commandId' => $commandId,
                'instanceId' => $instanceId,
                'timed' => $frequency ? 'true':'false',
                'frequency' => $frequency,
            ]);
            $result = $this->client->invokeCommand($request);

            $invokeId = UtilArr::get($result->body->toMap(),'InvokeId');
            if(!$invokeId){
                throw new \Exception('返回数据错误');
            }
            return $invokeId;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }

    public function commandRun(string $commandContent,array $instanceIds,string $type='RunShellScript',string $contentEncoding='PlainText',$regionId = ''):?string{
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new RunCommandRequest([
                'regionId' => $regionId,
                "instanceId" => $instanceIds,
                "commandContent" => $commandContent,
                "type" => $type,
                "contentEncoding" => $contentEncoding,
            ]);
            $result = $this->client->runCommand($request);

            $data =  $result->body->toMap();

            $invokeId = UtilArr::get($data,'InvokeId');
            if(!$invokeId){
                throw new \Exception('返回数据错误');
            }
            return $invokeId;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function commandInvocationsDescribe(string $invokeId='',string $commandId='',string $instanceId='',$regionId = ''):?array{
        $regionId = $this->getRegionId($regionId);
        try {
            $request = new DescribeInvocationsRequest([
                'regionId' => $regionId,
                "invokeId" => $invokeId,
                "commandId" => $commandId,
                "instanceId" => $instanceId,
            ]);
            $result = $this->client->describeInvocations($request);

            $data =  $result->body->toMap();

            $invocations = (array)UtilArr::get($data, 'Invocations.Invocation');
            $invokeInstances = (array)UtilArr::get($invocations, 'InvokeInstances.InvokeInstance');
            unset($invocations['InvokeInstances']);
            foreach ($invokeInstances as $invokeInstance){
                $invokeInstanceId = UtilArr::get($invokeInstance, 'InstanceId');
                if(!$invokeInstanceId){
                    continue;
                }

                $invocations['InvokeInstances'][$invokeInstanceId] = $invokeInstance;
            }
            unset($data['Invocations']);
            foreach ($invocations as $invocation){
                $invocationId = UtilArr::get($invocation, 'InvokeId');
                if(!$invocationId){
                    continue;
                }

                $data['Invocations'][$invocationId] = $invocation;
            }
            return $data;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function diskCreate(int $size,string $diskCategory=null,string $diskName=null,string $instanceId=null,string $zoneId=null, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new CreateDiskRequest([
                'regionId' => $regionId,
                'size' => $size,
                'diskCategory' => $diskCategory,
                'diskName' => $diskName,
                'instanceId' => $instanceId,
                'zoneId' => $instanceId ? null : $zoneId,
            ]);
            $result = $this->client->createDisk($request);

            $data =  $result->body->toMap();

            $id = UtilArr::get($data,'DiskId');
            if(!$id){
                throw new \Exception('返回数据错误');
            }


            $model = new AssetModel();
            $model->type ='disk';
            $model->asset_id = $id;

            $model->name = (string)$diskName;
            $model->region_id = (string)$regionId;
            $model->memory = $size;
            $model->bind_asset_id = $instanceId;


            $model->status = 'Pending';
            return $model;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }


    public function disksDescribe(array $diskIds=[],$instanceId=null, $regionId = ''):AssetModels
    {
        $regionId = $this->getRegionId($regionId);

        $models = new AssetModels();
        try {

            $request = new DescribeDisksRequest([
                'regionId' => $regionId,
                'diskIds' => !empty($diskIds) ? json_encode($diskIds) : null,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->describeDisks($request);

            $data = (array)UtilArr::get($result->body->toMap(), 'Disks.Disk');

            //dump($data);
            foreach ($data as $datum){
                $id = UtilArr::get($datum,'DiskId');
                if(!$id){
                    continue;
                }

                $model = new AssetModel();
                $model->type ='disk';
                $model->asset_id = $id;

                $model->name = UtilArr::get($datum,'DiskName');
                $model->region_id = (string)$regionId;
                $model->memory = UtilArr::get($datum,'Size');
                $model->bind_asset_id = UtilArr::get($datum,'InstanceId');
                $model->os = UtilArr::get($datum,'Device');
                $model->os_type = UtilArr::get($datum,'Type');


                $model->status = UtilArr::get($datum,'Status');

                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return $models;
    }

    public function diskDescribe(string $diskId, $regionId = ''):?AssetModel
    {
        $models = $this->disksDescribe([$diskId],null, $regionId);
        if(!$models){
            return null;
        }

        return $models->first(function ($item) use ($diskId){
            return  $item->asset_id == $diskId;
        });
    }

    public function diskUpdate(string $diskId,$name='', $regionId = ''): ?AssetModel{
        $regionId = $this->getRegionId($regionId);
        $model = $this->diskDescribe($diskId,$regionId);
        if (!$model) {
            throw new \Exception('云盘不存在');
            return null;
        }
        $name = trim($name);
        if(!$name){
            return $model;
        }
        try {
            $request = new ModifyDiskAttributeRequest([
                'regionId' => $regionId,
                'diskId' => $diskId,
                'diskName' => $name,
            ]);
            $result = $this->client->modifyDiskAttribute($request);

            if(!(bool)$result->body->toMap()){
                throw new \Exception('返回数据错误');
            }
            if($name ){
                $model->name = $name;
            }
            return $model;
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }

    public function diskAttach(string $diskId,string $instanceId,bool $bootable=false,bool $deleteWithInstance=false, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new AttachDiskRequest([
                'regionId' => $regionId,
                'diskId' => $diskId,
                'instanceId' => $instanceId,
                'bootable' => $bootable ? 'true' : 'false',
                'deleteWithInstance' => $deleteWithInstance ? 'true' : 'false',
            ]);
            $result = $this->client->attachDisk($request);


            return $this->diskDescribe($diskId);
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }
    public function diskDetach(string $diskId,string $instanceId, $regionId = ''):?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            $request = new DetachDiskRequest([
                'regionId' => $regionId,
                'diskId' => $diskId,
                'instanceId' => $instanceId,
            ]);
            $result = $this->client->detachDisk($request);


            return $this->diskDescribe($diskId);
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return null;
    }


    public function diskDelete(string $diskId, $regionId = ''):bool
    {
        $regionId = $this->getRegionId($regionId);
        $diskId = trim($diskId);
        if(!$diskId){
            return false;
        }
        if(!$this->diskDescribe($diskId)){
            return true;
        }
        try {
            $request = new DeleteDiskRequest([
                'regionId' => $regionId,
                'diskId' => $diskId,
            ]);
            $result = $this->client->deleteDisk($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }


    public function diskSnapshotCreate(string $diskId, string $name='', $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $diskId = trim($diskId);
        if (!$diskId) {
            return false;
        }
        if (!$this->diskDescribe($diskId)) {
            return false;
        }
        $name = trim($name);

        if ("" == $name) {
            $name = date("Ymd-His", time());
        }

        try {
            $request = new CreateSnapshotRequest([
                'snapshotName' => $name,
                'diskId' => $diskId,
            ]);
            $result = $this->client->createSnapshot($request);

            return (bool)$result->body->toMap();
        } catch (\Exception $exception) {
            $this->setError($exception->getMessage());
        }
        return false;
    }
}

